import {Source, SourceState} from "../Source";
import {Tile} from "./Tile";
import {FreeMap, LatLngBounds} from "../../";
import {Event} from "three";

export abstract class TileSource extends Source {

    protected tiles: Array<Tile> = [];

    public addTo(map: FreeMap): void {
        super.addTo(map);

        this.pushDataToLayer(this.tiles);

        this.map.eventDispatcher.addEventListener('panend', e => this.onPanEnd(e));
        this.map.eventDispatcher.addEventListener('zoomchange', e => this.onZoomChange(e));
    }

    requestData(): void {
        let zoom = Math.round(this.map.mapRenderer.camera.mapZoom);

        let bounds = this.map.mapRenderer.camera.getScreenBounds();

        let ns = this.queryViewBounds(bounds, zoom);

        this.forEachTileLoadTile(ns[0], ns[1], ns[2], ns[3]);
    }

    forEachTileLoadTile(startX: number, endX: number, startY: number, endY: number) {
        this.tiles = [];

        let z = Math.round(this.map.mapRenderer.camera.mapZoom);

        for (let x = startX; x <= endX; x++) {
            for (let y = startY; y <= endY; y++) {
                let url = this.getURL(x, y, z);
                let tile = new Tile(x, y, z, url);
                tile.bbox = this.getBounds(x, y, z);
                this.tiles.push(tile);
            }
        }

        this.pushDataToLayer(this.tiles);
    }

    abstract queryViewBounds(bounds: LatLngBounds, zoom: number): number[]

    abstract getURL(x: number, y: number, z: number): string

    abstract getBounds(x: number, y: number, z: number): LatLngBounds

    private onPanEnd(e: Event) {
        this.requestData();
    }

    private onZoomChange(e: any) {
        if (this.state == SourceState.LOADING) {
            return
        }
        let floorZoom = Math.round(e.zoom);
        let roundZoom = Math.round(e.lastZoom);
        if (floorZoom != roundZoom) {
            this.requestData();
        }
    }
}
